using System;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Configuration;

namespace Stunt.Nutss.Signaling
{

	/// <summary>
	/// Common functionality used by the SlingServer and SlingProvider client.
	/// </summary>
	internal class SlingCommon
	{

		#region Enum

		/// <summary>Types of proxy resolution methods</summary>
		private enum ProxyType
		{
			PROXY_JSTUNT,
			PROXY_DOMAIN,
			PROXY_FIXED
		}

		#endregion

		#region Variables

		/// <summary>Proxy resolution method</summary>
		static ProxyType proxyType;
		/// <summary>Port the server listens on</summary>
		static int proxyPort = 51641;
		/// <summary>Port the server listens on</summary>
		static IPEndPoint proxyFixedAddress = null;

		/// <summary>Matches unsafe DNS characters</summary>
		private static string regexPattern = "(\\.|[^A-Za-z0-9.])+";

		#endregion

		#region Constructor

		/// <summary>
		/// Load the configuration.
		/// </summary>
		static SlingCommon()
		{

			try
			{
				//---- Proxy type
				string k = ConfigurationManager.AppSettings["ProxyResolution"];

				if (k != null)

					if (k == "JSTUNT")
						proxyType = ProxyType.PROXY_JSTUNT;

					else if (k == "DOMAIN")
						proxyType = ProxyType.PROXY_DOMAIN;

					else if (k == "FIXED")
						proxyType = ProxyType.PROXY_FIXED;
				//---- Port
				k = ConfigurationManager.AppSettings["ProxyPort"];

				if (k != null)
					proxyPort = Int32.Parse(k);
				//---- Proxy address
				k = ConfigurationManager.AppSettings["ProxyAddress"];

				if (k != null)
					proxyFixedAddress = new IPEndPoint(Dns.GetHostAddresses(k)[0], proxyPort);
			}

			catch (Exception e)
			{
				Debug.WriteLine(e.StackTrace);
			}
		}

		#endregion

		#region ResolveProxy

		/// <summary>
		/// Returns the EndPoint of the JSlingServer that maintains authoritative location information for
		/// the given ID.
		/// </summary>
		/// <param name="id">ID for which a proxy node is sought</param>
		/// <returns>IP address and port of the proxy</returns>
		/// <throws>ExtendedSocketException if proxy cannot be found</throws>
		internal static IPEndPoint ResolveProxy(NutssUri id)
		{

			switch (proxyType)
			{

				case ProxyType.PROXY_JSTUNT:
					return ResolveProxyJSTUNT(id);

				case ProxyType.PROXY_DOMAIN:
					return ResolveProxyDomain(id);

				case ProxyType.PROXY_FIXED:
					return ResolveProxyFixed(id);
				default: throw new ExtendedSocketException("Don't know how to resolve: " + id);
			}
		}

		#endregion

		#region Internals

		/// <summary>Formats an ID to a clean DNS address</summary>
		/// <param name="id">ID for which a proxy node is sought</param>
		/// <returns>DNS name for the ID</returns>
		private static string DNSScrub(NutssUri id)
		{
			Regex regEx = new Regex(regexPattern, RegexOptions.ExplicitCapture);
			string s = "X" + regEx.Replace(id.ToString(), ".") + "X";
			return s;
		}

		/// <summary>
		/// Returns the EndPoint of the JSlingServer that maintains authoritative location
		/// information for the given ID. Proxies are resolved through the JSTUNT NUTSS DNS hierarchy.
		/// </summary>
		/// <param name="id">ID for which a proxy node is sought</param>
		/// <returns>IP address and port of the proxy</returns>
		private static IPEndPoint ResolveProxyJSTUNT(NutssUri id)
		{
			//HACK to run on localhost
#if DEBUG
			string sId = DNSScrub(id);
            IPAddress address = new IPAddress(new byte[] { 127, 0, 0, 1 });
			return new IPEndPoint(address, proxyPort);
#else
            return new IPEndPoint(Dns.GetHostAddresses(DNSScrub(id) + ".jstunt.nutss.net")[0], proxyPort);
#endif
		}

		/// <summary>
		/// Returns the EndPoint of the JSlingServer that maintains authoritative location information for
		/// the given ID. Proxies are resolved through individual domain hierarchies.
		/// </summary>
		/// <param name="id">ID for which a proxy node is sought</param>
		/// <returns>IP address and port of the proxy</returns>
		private static IPEndPoint ResolveProxyDomain(NutssUri id)
		{
			return new IPEndPoint(IPAddress.Parse(id.ToString().Split(new char[] { '@' })[1]), proxyPort);
		}

		/// <summary>
		/// Returns the EndPoint of the JSlingServer that maintains authoritative location information for
		/// the given ID. Proxies are resolved to a fixed address.
		/// </summary>
		/// <param name="id">ID for which a proxy node is sought</param>
		/// <returns>IP address and port of the proxy</returns>
		private static IPEndPoint ResolveProxyFixed(NutssUri id)
		{
			return proxyFixedAddress;
		}

		#endregion

	}
}